
from collections.abc import Mapping
import paramiko
from paramiko import SSHClient
import time
import re
from ssh.exceptions import *
from paramiko.channel import ChannelFile, ChannelStderrFile, ChannelStdinFile
import logging
from PySide2.QtCore import *

MAX_BUFFER = 65535

class Signal_ssh_client(QObject):
    send_logs = Signal(str)


class Ssh_client(SSHClient):

    def __init__(self,hostname:str,username:str,password:str,port:int = 22):
        super().__init__()

        self.log = logging.getLogger('app_log')
        self.signal = Signal_ssh_client()
        self.hostname = hostname
        self.username = username
        self.password = password
        self.port = port
        self.set_missing_host_key_policy(paramiko.AutoAddPolicy)
        self.channel : paramiko.Channel = None


    def __del__(self):
        if self.channel != None:
            self.channel.close()

    def connect_server(self) -> bool:
        try:
            self.connect(self.hostname,self.port,self.username,self.password)
            log_text = f'连接服务器成功!{self.hostname}'
            self.log.info(log_text)
            return True
        except Exception as e:
            log_text = f'连接服务器失败!{self.hostname}\n{e}'
            self.log.error(log_text)
            return False
        
    
    def exec_cmd(self,cmd:str):
        # 不知道为啥 华为 dis cur 会卡死
        cmd = cmd.strip() + '\n'
        return self.exec_command(cmd)


    def init_invoke(self) -> bool:
        # 非必要 不用invoke 速度太慢了
        # exec_command 速度快多了
        try:
            self.channel = self.invoke_shell()
            log_text = f'channel初始化成功!{self.hostname}'
            self.log.info(log_text)
            return True
        except Exception as e:
            log_text = f'channel初始化失败!{self.hostname}\n{e}'
            self.log.error(log_text)
            return False
        
   
    def send_command(self,cmd:str):
        cmd = cmd.strip() +'\n'
        while cmd:
            if isinstance(cmd,str):
                cmd = cmd.encode('utf-8')
            sent = self.channel.send(cmd)
            cmd = cmd[sent:]
    
    def send_commands(self,cmds:list[str]) -> str:
        echo = []
        for cmd in cmds:
            self.send_command(cmd)
            echo.append(self.read_channel_timing())
            # 可以考虑在这里发射一个信号及时回显结果
        echos =''
        for text in echo:
            echos += text
        self.log.info(echos)
        return echos



    def read_buffer(self) -> str:
        """读取一次通道数据"""
        if self.channel is None:
            raise ChannelError("Attempt to read, but there is no active channel.")
        output = ""
        if self.channel.recv_ready():
            outbuf = self.channel.recv(MAX_BUFFER)
            if len(outbuf) == 0:
                raise ChannelError("Channel stream closed by remote device.")
            output += outbuf.decode()
        return output
    
    
    def read_channel(self) -> str:
        """Generic handler that will read all the data from given channel."""
        new_data = self.read_all_buffer()

        start = time.time()
        # Data blocks shouldn't end in '\r' (can cause problems with normalize_linefeeds)
        # Only do the extra read if '\n' exists in the output
        # this avoids devices that only use \r.
        while ("\n" in new_data) and (time.time() - start < 1.0):
            if new_data[-1] == "\r":
                time.sleep(0.01)
                new_data += self.read_all_buffer()
            else:
                break
        return new_data
        

    def read_all_buffer(self) -> str:
        """Read all of the available data from the channel."""
        if self.channel is None:
            raise ChannelError("Attempt to read, but there is no active channel.")
        output = ""
        while True:
            new_output = self.read_buffer()
            output += new_output
            if new_output == "":
                break
        return output
    
    def read_channel_timing(self,last_read:float = 2,read_timeout:float = 120):
        loop_delay = 0.1
        channel_data = ""
        start_time = time.time()

        # Set read_timeout to 0 to never timeout
        while (time.time() - start_time < read_timeout) or (not read_timeout):
            time.sleep(loop_delay)
            new_data = self.read_channel()
            # gather new output
            if new_data:
                channel_data += new_data
            # if we have some output, but nothing new, then do the last read
            elif channel_data != "":
                # Make sure really done (i.e. no new data)
                time.sleep(last_read)
                new_data = self.read_channel()
                if not new_data:
                    break
                else:
                    channel_data += new_data
        else:
            ReadChannelTimeout('读取channel数据超时！')
        return channel_data


    